#pragma once

#include <iostream>
#include <string>
#include <memory>
#include <cerrno>
#include <functional>
#include <unordered_map>
#include "Log.hpp"
#include "nocopy.hpp"
#include "epoller.hpp"
#include "Socket.hpp"
#include "Comm.hpp"

class Connection;
class TcpServer;

uint32_t EVENT_IN = (EPOLLIN | EPOLLET);
uint32_t EVENT_OUT = (EPOLLOUT | EPOLLET);
const static int g_buffer_size = 128;

using func_t = std::function<void(std::weak_ptr<Connection>)>;
using except_func = std::function<void(std::weak_ptr<Connection>)>;

class Connection
{
public:
    Connection(int sock)
        : _sock(sock)
    {
    }
    void SetHandler(func_t recv_cb, func_t send_cb, except_func except_cb)
    {
        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }
    void AppendInBuffer(const std::string &info)
    {
        _inbuffer += info;
    }
    void AppendOutBuffer(const std::string &info)
    {
        _outbuffer += info;
    }
    std::string &Inbuffer()
    {
        return _inbuffer;
    }
    std::string &Outbuffer()
    {
        return _outbuffer;
    }
    void SetWeakPtr(std::weak_ptr<TcpServer> tcp_server_ptr)
    {
        _tcp_server_ptr = tcp_server_ptr;
    }
    ~Connection()
    {
    }
    int FdSock()
    {
        return _sock;
    }

private:
    int _sock;
    std::string _inbuffer;
    std::string _outbuffer;

public:
    func_t _recv_cb;
    func_t _send_cb;
    except_func _except_cb;

    // 添加一个回指指针
    // std::weak_ptr<TcpServer> _tcp_server_ptr; // std::weak_ptr<> // bug??
    std::weak_ptr<TcpServer> _tcp_server_ptr; // std::weak_ptr<>

    std::string _ip;
    uint16_t _port;
};

class TcpServer : public std::enable_shared_from_this<TcpServer>, public nocopy
{
    static const int num = 64;

public:
    TcpServer(uint16_t port, func_t OnMessage)
        : _port(port), _quit(true), _listensock_ptr(new Sock()), _epoller_ptr(new Epoller()), _OnMessage(OnMessage)
    {
    }
    // // 链接管理器
    // void Accepter(std::weak_ptr<Connection> conn)
    // {
    //     auto connection = conn.lock();
    //     while (true)
    //     {
    //         struct sockaddr_in peer;
    //         socklen_t len = sizeof(peer);
    //         int sock = ::accept(connection->FdSock(), (sockaddr *)&peer, &len);
    //         if (sock > 0)
    //         {
    //             uint16_t peerport = ntohs(peer.sin_port);
    //             char ipbuf[128];
    //             inet_ntop(AF_INET, &peer.sin_addr.s_addr, ipbuf, sizeof(ipbuf));
    //             lg(Debug, "get a new client, get info-> [%s:%d], sockfd : %d", ipbuf, peerport, sock);
    //             SetNonBlockOrDie(sock);
    //             AddConnection(sock, EPOLLIN, std::bind(&TcpServer::Recver, this, std::placeholders::_1),
    //                           std::bind(&TcpServer::Sender, this, std::placeholders::_1),
    //                           std::bind(&TcpServer::Excepter, this, std::placeholders::_1), ipbuf, peerport);
    //         }
    //         else
    //         {
    //             if (errno == EWOULDBLOCK)
    //                 break;
    //             else if (errno == EINTR)
    //                 continue;
    //             else
    //                 break;
    //         }
    //     }
    // }
    // 事件管理器
    void Recver(std::weak_ptr<Connection> conn)
    {
        if (conn.expired())
            return;
        auto connection = conn.lock();
        int sock = connection->FdSock();
        while (true)
        {
            char buffer[g_buffer_size];
            memset(buffer, 0, sizeof(buffer));
            ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0); // 非阻塞读取
            if (n > 0)
            {
                connection->AppendInBuffer(buffer);
            }
            else if (n == 0)
            {
                lg(Warning, "sockfd: %d, client info %s:%d recv error...", sock, connection->_ip.c_str(), connection->_port);
                connection->_except_cb(connection);
                return;
            }
            else
            {
                if (errno = EWOULDBLOCK)
                    break;
                else if (errno = EINTR)
                    continue;
                else
                {
                    lg(Warning, "sockfd: %d, client info %s:%d recv error...", sock, connection->_ip.c_str(), connection->_port);
                    connection->_except_cb(connection);
                    return;
                }
            }
        }
        // 数据有了，但是不一定全，1. 检测 2. 如果有完整报文，就处理
        _OnMessage(connection); // 读到的sock所有的数据connection
    }

    void Sender(std::weak_ptr<Connection> conn)
    {
        if (conn.expired())
            return;
        auto connection = conn.lock();
        auto &outbuffer_str = connection->Outbuffer();
        while (true)
        {
            ssize_t n = send(connection->FdSock(), outbuffer_str.c_str(), outbuffer_str.size(), 0);
            if (n > 0)
            {
                outbuffer_str.erase(0, n);
                if (outbuffer_str.empty())
                    break;
            }
            else if (n == 0)
            {
                break;
                ;
            }
            else
            {
                if (errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    WARN("sockfd: %d ,client info %s:%d send error...", connection->FdSock(), connection->_ip.c_str(), connection->_port);
                    connection->_except_cb(connection);
                    return;
                }
            }
        }
        if (!outbuffer_str.empty())
        {
            // 开启对读事件的关心
            EnableEvent(connection->FdSock(), true, true);
        }
        else
        {
            // 关闭对读事件的关心
            EnableEvent(connection->FdSock(), true, false);
        }
    }
    void EnableEvent(int sock, bool readable, bool writeable)
    {
        uint32_t event = 0;
        event |= ((readable ? EVENT_IN : 0) | (writeable ? EVENT_OUT : 0) | EPOLLET);
        _epoller_ptr->EpllerUpdate(EPOLL_CTL_MOD, sock, event);
    }
    void Excepter(std::weak_ptr<Connection> conn)
    {
        if (conn.expired())
            return;
        auto connection = conn.lock();

        int fd = connection->FdSock();

        lg(Warning, "Excepter hander sockfd: %d, client info %s:%d excepter handler",
           connection->FdSock(), connection->_ip.c_str(), connection->_port);

        // 1. 移除对特定fd的关心
        // EnableEvent(connection->SockFd(), false, false);
        _epoller_ptr->EpllerUpdate(EPOLL_CTL_DEL, fd, 0);
        // 2. 关闭异常的文件描述符
        lg(Debug, "close %d done...\n", fd);
        close(fd);
        // 3. 从unordered_map中移除
        lg(Debug, "remove %d from _connections...\n", fd);
        _connections.erase(fd);
    }

    void AddConnection(int sock, uint32_t event, func_t recv_cb, func_t send_cb, except_func except_cb,
                       const std::string &ip = "0.0.0.0", uint16_t port = 0)
    {
        // 1. 给sock也建立一个connection对象，将listensock添加到Connection中，同时，listensock和Connecion放入_connections
        // std::shared_ptr<Connection> new_connection = std::make_shared<Connection>(sock, std::shared_ptr<TcpServer>(this));
        std::shared_ptr<Connection> new_connection(new Connection(sock));
        new_connection->SetHandler(recv_cb, send_cb, except_cb);
        new_connection->_ip = ip;
        new_connection->_port = port;
        new_connection->SetWeakPtr(shared_from_this()); // shared_from_this(): 返回当前对象的shared_ptr

        // 2.添加到unordered_map
        _connections.insert(std::make_pair(sock, new_connection));

        // 3.添加对应的事件到内核中
        _epoller_ptr->EpllerUpdate(EPOLL_CTL_ADD, sock, event);
    }
    void Init()
    {
        _listensock_ptr->Socket();
        _listensock_ptr->Bind(_port);
        _listensock_ptr->Listen();
        // 设置非阻塞
        SetNonBlockOrDie(_listensock_ptr->Fd());

        lg(Info, "create listen socket success: %d", _listensock_ptr->Fd());

        //AddConnection(_listensock_ptr->Fd(), EVENT_IN, std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
    }
    bool IsConnectionSafe(int fd)
    {
        auto pos = _connections.find(fd);
        if (pos == _connections.end())
            return false;
        else
            return true;
    }
    void Dispatcher(int timeout)
    {
        // 等待事件就绪
        int n = _epoller_ptr->EpollerWait(rev, num, timeout);
        for (int i = 0; i < n; i++)
        {
            uint32_t event = rev[i].events;
            int sock = rev[i].data.fd;
            // 统一把事件异常转换成为读写问题
            if (event & EPOLLERR)
                event |= (EPOLLIN | EPOLLOUT);
            if (event & EPOLLHUP)
                event |= (EPOLLIN | EPOLLOUT);
            // 只需要处理EPOLLIN EPOLLOUT
            if (IsConnectionSafe(sock) && (event & EPOLLIN))
            {
                if (_connections[sock]->_recv_cb)
                    _connections[sock]->_recv_cb(_connections[sock]);
            }
            if ((event & EPOLLOUT) && IsConnectionSafe(sock))
            {
                if (_connections[sock]->_send_cb)
                    _connections[sock]->_send_cb(_connections[sock]);
            }
        }
    }
    void Loop()
    {
        _quit = false;
        while (!_quit)
        {
            // 一直进行事件派发
            Dispatcher(-1);
            PrintConnection();
        }
        _quit = true;
    }
    void PrintConnection()
    {
        std::cout << "_connections fd list: ";
        for (auto &connection : _connections)
        {
            std::cout << connection.second->FdSock() << ",";
            std::cout << connection.second->Inbuffer().c_str();
        }
        std::cout << std::endl;
    }
    ~TcpServer()
    {
        _listensock_ptr->Close();
    }

private:
    std::shared_ptr<Sock> _listensock_ptr;
    std::shared_ptr<Epoller> _epoller_ptr;
    struct epoll_event rev[num];
    uint16_t _port;
    bool _quit;
    std::unordered_map<int, std::shared_ptr<Connection>> _connections;

    func_t _OnMessage;
};